﻿using System;
using System.ComponentModel;
using System.Security.Permissions;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using DotNetNuke.Entities.Portals;
using HeroldIT.Dnn.JQuery.Extras;

namespace HeroldIT.Dnn.Modules.WebControls
{
    /// <summary>
    /// A control for choosing a file in the portal.
    /// </summary>
    [ControlValueProperty("Text")]
    [DefaultProperty("Text")]
    [ValidationProperty("Text")]
    [ParseChildren(true, "Text")]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    public class FileBrowser : Control, ITextControl, INamingContainer
    {
        #region control fields

        /// <summary>
        /// Holds the WindowedDialog control for the file browser.
        /// </summary>
        protected WindowedDialog wdlgFileBrowser;

        /// <summary>
        /// Holds the Textbox control for displaying the file name.
        /// </summary>
        protected TextBox txtFileName;

        /// <summary>
        /// Holds the button for displaying the dialog.
        /// </summary>
        protected Button btnChooseFile;

        /// <summary>
        /// Holds the button for deselecting the file.
        /// </summary>
        protected Button btnDeselect;

        #endregion

        #region properties

        #region control settings

        /// <summary>
        /// Gets or sets the control's css class.
        /// </summary>
        public string CssClass { get; set; }

        /// <summary>
        /// Gets or sets the selected file.
        /// </summary>
        public string Text
        {
            get
            {
                this.EnsureChildControls();
                return this.txtFileName.Text;
            }
            set
            {
                this.EnsureChildControls();
                this.txtFileName.Text = value;
            }
        }

        /// <summary>
        /// Gets or sets the selected file taking the configured <see cref="RootFolder"/> and Portal root into account.
        /// </summary>
        string ITextControl.Text
        {
            get
            {
                return String.IsNullOrEmpty(this.Text) ? null : this.PortalSettings.HomeDirectory + (this.RootFolder ?? String.Empty) + (!String.IsNullOrEmpty(this.Text) && this.Text[0] == '/' ? this.Text.Substring(1) : this.Text);
            }
            set
            {
                string pathPrefix = this.PortalSettings.HomeDirectory + (this.RootFolder ?? String.Empty);
                if (null == value || value.StartsWith(pathPrefix))
                {
                    this.Text = null == value ? null : value.Substring(pathPrefix.Length);
                }
                else
                {
                    throw new ArgumentException("The file is not contained in the specified RootFolder.", "value");
                }
            }
        }

        private Unit width = Unit.Pixel(150);

        /// <summary>
        /// Gets or sets the control's width.
        /// </summary>
        public Unit Width
        {
            get { return this.width; }
            set { this.width = value; }
        }

        private string chooseFileButtonCaption = "Choose file";

        /// <summary>
        /// Gets or sets the caption for the choose file button.
        /// </summary>
        public string ChooseFileButtonCaption
        {
            get { return this.chooseFileButtonCaption; }
            set { this.chooseFileButtonCaption = value; }
        }

        /// <summary>
        /// Gets or sets the css class for the choose file button.
        /// </summary>
        public string ChooseFileButtonCssClass { get; set; }

        private string chooseFileButtonImageUrl = "~/images/icon_filemanager_16px.gif";

        /// <summary>
        /// Gets or sets the image for the choose file button.
        /// </summary>
        public string ChooseFileButtonImageUrl
        {
            get { return this.chooseFileButtonImageUrl; }
            set { this.chooseFileButtonImageUrl = value; }
        }

        private bool chooseFileUseLinkButton = true;

        /// <summary>
        /// Gets or sets whether to use a LinkButton instead of a Button control for
        /// the choose file button.
        /// </summary>
        public bool ChooseFileUseLinkButton
        {
            get { return this.chooseFileUseLinkButton; }
            set { this.chooseFileUseLinkButton = value; }
        }

        /// <summary>
        /// Gets or sets whether to show the file name.
        /// </summary>
        public bool ShowFileName { get; set; }

        private string fileNameCssClass = "NormalTextBox";

        /// <summary>
        /// Gets or sets the css class for the file name label.
        /// </summary>
        public string FileNameCssClass
        {
            get { return this.fileNameCssClass; }
            set { this.fileNameCssClass = value; }
        }

        private bool showDeselectButton = true;

        /// <summary>
        /// Gets or sets whether to show the deselect button.
        /// </summary>
        public bool ShowDeselectButton
        {
            get { return this.showDeselectButton; }
            set { this.showDeselectButton = value; }
        }

        private string deselectButtonCaption = "No file";

        /// <summary>
        /// Gets or sets the caption for the deselect button.
        /// </summary>
        public string DeselectButtonCaption
        {
            get { return this.deselectButtonCaption; }
            set { this.deselectButtonCaption = value; }
        }

        /// <summary>
        /// Gets or sets the css class for the deselect button.
        /// </summary>
        public string DeselectButtonCssClass { get; set; }

        private string deselectButtonImageUrl = "~/images/delete.gif";

        /// <summary>
        /// Gets or sets the image for the deselect button.
        /// </summary>
        public string DeselectButtonImageUrl
        {
            get { return this.deselectButtonImageUrl; }
            set { this.deselectButtonImageUrl = value; }
        }

        private bool deselectUseLinkButton = true;

        /// <summary>
        /// Gets or sets whether to use a LinkButton instead of a Button control for
        /// the deselect button.
        /// </summary>
        public bool DeselectUseLinkButton
        {
            get { return this.deselectUseLinkButton; }
            set { this.deselectUseLinkButton = value; }
        }

        #endregion

        #region dialog settings

        /// <summary>
        /// Gets or sets the file browser's title.
        /// </summary>
        public string DialogTitle { get; set; }

        private string rootFolder;

        /// <summary>
        /// Gets or sets the file browser's root folder.
        /// </summary>
        public string RootFolder
        {
            get { return this.rootFolder; }
            set 
            {
                this.rootFolder = value;
                if (!String.IsNullOrEmpty(this.rootFolder) && !this.rootFolder.EndsWith("/", StringComparison.Ordinal))
                {
                    this.rootFolder += "/";
                }
            }
        }

        /// <summary>
        /// Gets or sets the file browser's initial folder.
        /// </summary>
        /// <remarks>
        /// Set this to <c>null</c> to enable automatic folder selection depending
        /// on the selected file.
        /// </remarks>
        public string InitialFolder { get; set; }

        private bool showFolders = true;

        /// <summary>
        /// Gets or sets whether the file browser will display folders.
        /// </summary>
        public bool ShowFolders
        {
            get { return this.showFolders; }
            set { this.showFolders = value; }
        }

        /// <summary>
        /// Gets or sets the filename filter for the file browser.
        /// </summary>
        public virtual string Filters { get; set; }

        /// <summary>
        /// Gets or sets the maximum image width for resizing.
        /// </summary>
        public int MaxImageWidth { get; set; }

        /// <summary>
        /// Gets or sets the maximum image height for resizing.
        /// </summary>
        public int MaxImageHeight { get; set; }

        private int imageQuality = 90;

        /// <summary>
        /// Gets or sets the image quality for resized images.
        /// </summary>
        public int ImageQuality
        {
            get { return this.imageQuality; }
            set { this.imageQuality = value; }
        }

        /// <summary>
        /// Gets or sets the matte color for resized images.
        /// </summary>
        public string MatteColor { get; set; }

        #endregion

        #region helpers

        /// <summary>
        /// Gets the dialog title or a default title, if none is set.
        /// </summary>
        protected string DialogTitleEnsured
        {
            get { return String.IsNullOrEmpty(this.DialogTitle) ? "Select file..." : this.DialogTitle; }
        }

        private PortalSettings portalSettings;

        /// <summary>
        /// Gets the current portal settings.
        /// </summary>
        protected PortalSettings PortalSettings
        {
            get
            {
                if (null == this.portalSettings)
                {
                    this.portalSettings = PortalController.GetCurrentPortalSettings();
                }
                return this.portalSettings;
            }
        }

        #endregion

        #endregion

        #region page lifecycle

        /// <summary>
        /// Called by the ASP.NET page framework to notify server controls that use composition-based implementation to create any child controls they contain in preparation for posting back or rendering.
        /// </summary>
        protected override void CreateChildControls()
        {
            this.txtFileName = new TextBox
                                   {
                                       ID = "txtFileName",
                                       CssClass = this.FileNameCssClass,
                                       EnableViewState = false,
                                       ReadOnly = false,
                                       Width = this.Width
                                   };
            this.Controls.Add(this.txtFileName);
            this.wdlgFileBrowser = new WindowedDialog
                                       {
                                           ID = "wdlgFileBrowser",
                                           AutoOpen = false,
                                           CenterScreen = true,
                                           Modal = true,
                                           FrameName = this.ClientID,
                                           DialogHeight = 400,
                                           DialogWidth = 600,
                                           Resizable = true
                                       };
            this.Controls.Add(this.wdlgFileBrowser);
            this.wdlgFileBrowser.ClientEvents.Result = "jQuery('#" + this.txtFileName.ClientID + "').val(ui.dialogResult())";

            base.CreateChildControls();
        }

        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (!this.ShowFileName)
            {
                this.txtFileName.Style[HtmlTextWriterStyle.Display] = "none";
            }
            this.txtFileName.ReadOnly = true;
            this.txtFileName.Width = this.Width;
            this.btnChooseFile = new Button
                                     {
                                         ID = "btnChooseFile",
                                         CssClass = ChooseFileButtonCssClass,
                                         Text =
                                             String.IsNullOrEmpty(this.ChooseFileButtonCaption)
                                                 ? "Choose file"
                                                 : this.ChooseFileButtonCaption,
                                         UseLinkButton = this.ChooseFileUseLinkButton,
                                         ImageUrl = this.ChooseFileButtonImageUrl,
                                         ImageBorderWidth = 0,
                                         CausesValidation = false,
                                         OnClientClick = "var v=jQuery('#"
                                                         + this.txtFileName.ClientID
                                                         + "').val();if(!!v){var p='fbcurrentfile='+escape(v);var dialog=jQuery.ui.windowedDialog.instances['"
                                                         + this.ClientID
                                                         + "'];if(!!dialog){if(dialog.options.url.indexOf('fbcurrentfile=')>=0){dialog.options.url=dialog.options.url.replace(/fbcurrentfile=[^&]*/,p)}else{dialog.options.url+='&'+p}}}"
                                                         + this.wdlgFileBrowser.ClientMethods.Open()
                                                         + ";return false"
                                     };
            this.Controls.Add(this.btnChooseFile);
            if (this.ShowDeselectButton)
            {
                this.btnDeselect = new Button
                                       {
                                           ID = "btnDeselect",
                                           CssClass = DeselectButtonCssClass,
                                           Text =
                                               String.IsNullOrEmpty(this.DeselectButtonCaption)
                                                   ? "No file"
                                                   : this.DeselectButtonCaption,
                                           UseLinkButton = this.DeselectUseLinkButton,
                                           ImageUrl = this.DeselectButtonImageUrl,
                                           ImageBorderWidth = 0,
                                           CausesValidation = false,
                                           OnClientClick =
                                               "jQuery('#" + this.txtFileName.ClientID + "').val('');return false"
                                       };
                this.Controls.Add(this.btnDeselect);
            }
            StringBuilder sb = new StringBuilder(VirtualPathUtility.ToAbsolute("~/DesktopModules/HeroldIT.ModuleFramework/FileBrowser/FileBrowser.aspx")
                + "?tabid=" + this.PortalSettings.ActiveTab.TabID + "&fbtitle=" + HttpUtility.UrlEncode(this.DialogTitleEnsured));
            if (!String.IsNullOrEmpty(this.RootFolder))
            {
                sb.Append("&fbrootfolder=" + HttpUtility.UrlEncode(this.RootFolder));
            }
            if (null == this.InitialFolder)
            {
                if (!String.IsNullOrEmpty(this.Text))
                {
                    int index = this.Text.LastIndexOf('/');
                    this.InitialFolder = index >= 0
                        ? this.Text.Substring(0, index)
                        : null;
                }
            }
            if (!String.IsNullOrEmpty(this.InitialFolder))
            {
                sb.Append("&fbcurrentfolder=" + HttpUtility.UrlEncode(this.InitialFolder));
            }
            if (!this.ShowFolders)
            {
                sb.Append("&fbshowfolders=0");
            }
            if (!String.IsNullOrEmpty(this.Filters))
            {
                sb.Append("&fbfilter=" + HttpUtility.UrlEncode(this.Filters));
            }
            if (this.MaxImageWidth > 0)
            {
                sb.Append("&fbmaximagewidth=" + this.MaxImageWidth);
            }
            if (this.MaxImageHeight > 0)
            {
                sb.Append("&fbmaximageheight=" + this.MaxImageHeight);
            }
            if (this.ImageQuality >= 0 && this.ImageQuality <= 100)
            {
                sb.Append("&fbimagequality=" + this.ImageQuality);
            }
            if (!String.IsNullOrEmpty(this.MatteColor))
            {
                sb.Append("&fbmattecolor=" + HttpUtility.UrlEncode(this.MatteColor));
            }
            this.wdlgFileBrowser.Url = sb.ToString();
        }

        /// <summary>
        /// Sends server control content to a provided <see cref="T:System.Web.UI.HtmlTextWriter"/> object, which writes the content to be rendered on the client.
        /// </summary>
        /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param>
        protected override void Render(HtmlTextWriter writer)
        {
            bool hasClass = !String.IsNullOrEmpty(this.CssClass);
            if (hasClass)
            {
                writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssClass);
                writer.RenderBeginTag(HtmlTextWriterTag.Div);
            }
            base.Render(writer);
            if (hasClass)
            {
                writer.RenderEndTag();
            }
        }

        #endregion
    }
}